/**
 * @file bsp_print.c
 * @author zdk
 * @brief 调试打印支持，输出到UART2
 * @version 0.1
 * @date 2021-07-03
 * 
 * BSD 3-Clause License
 * 
 * Copyright (c) 2021, water_zhang
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 * 
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 * 
 * * Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * 
 */

#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "bsp_print.h"
#include "stm32f4xx.h"

#define UART_TX_PORT GPIOA
#define UART_TX_PIN GPIO_PIN_2
#define UART_RX_PORT GPIOA
#define UART_RX_PIN GPIO_PIN_3

#define PRINT_BUF_MAX (512)
static uint8_t print_buf[PRINT_BUF_MAX];
static uint8_t recv;
static UART_HandleTypeDef huart2;

static Bsp_Print_Recv_Callback usr_cb = NULL;
static void *usr_data;

/**
 * @brief 串口外设IO口初始化，在Bsp_Print_Init中回调
 * 
 * @param huart 触发回调的串口外设句柄
 */
static void Bsp_Print_UartMspInitCallback(UART_HandleTypeDef *huart)
{
    GPIO_InitTypeDef gpio_init;
    gpio_init.Alternate = GPIO_AF7_USART2;
    gpio_init.Speed = GPIO_SPEED_FREQ_LOW;
    gpio_init.Mode = GPIO_MODE_AF_PP;

    //使能GPIO口时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();

    //初始化TX
    gpio_init.Pin = UART_TX_PIN;
    gpio_init.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(UART_TX_PORT, &gpio_init);

    //初始化RX
    gpio_init.Pin = UART_RX_PIN;
    gpio_init.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(UART_TX_PORT, &gpio_init);

    //使能接收中断
    HAL_NVIC_SetPriority(USART2_IRQn, 5, 0);
    HAL_NVIC_EnableIRQ(USART2_IRQn);
}

/**
 * @brief 串口接收处理回调
 * 
 * @param huart 触发回调的串口外设句柄 
 */
static void Bsp_Print_UartRecvCallback(UART_HandleTypeDef *huart)
{
    if (huart2.Instance == huart->Instance)
    {
        //调用用户串口接收回调
        if(usr_cb)
        {
            usr_cb(recv, usr_data);
        }

        //重新使能串口接收
        HAL_UART_Receive_IT(&huart2, &recv, 1);
    }
}

/**
 * @brief 初始化串口2调试输出
 * 
 * @return uint8_t 1=成功，0=失败
 */
uint8_t Bsp_Print_Init(void)
{
    //使能串口外设时钟
    __HAL_RCC_USART2_CLK_ENABLE();

    //初始化串口外设，并注册IO初始化回调
    huart2.Instance = USART2;
    huart2.Init.BaudRate = 115200;
    huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart2.Init.Mode = UART_MODE_TX_RX;
    huart2.Init.OverSampling = UART_OVERSAMPLING_16;
    huart2.Init.Parity = UART_PARITY_NONE;
    huart2.Init.StopBits = UART_STOPBITS_1;
    huart2.Init.WordLength = UART_WORDLENGTH_8B;
    HAL_UART_RegisterCallback(&huart2, HAL_UART_MSPINIT_CB_ID, Bsp_Print_UartMspInitCallback);
    if (HAL_UART_Init(&huart2) != HAL_OK)
    {
        return 0;
    }

    //注册串口接收中断处理回调
    HAL_UART_RegisterCallback(&huart2, HAL_UART_RX_COMPLETE_CB_ID, Bsp_Print_UartRecvCallback);

    //使能串口接收
    HAL_UART_Receive_IT(&huart2, &recv, 1);

    return 1;
}

/**
 * @brief 注册字节接收处理回调，在中断中执行，写的快点
 * 
 * @param cb 回调函数，可以是NULL
 * @param usr 用户自定义数据
 */
void Bsp_Print_RegisterRecvCallback(Bsp_Print_Recv_Callback cb, void *usr)
{
    usr_cb = cb;
    usr_data = usr;
}

/**
 * @brief 打印字符
 * 
 * @param ch 字符
 */
void Bsp_Print_Putc(const char ch)
{
    HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 10);
}

/**
 * @brief 打印字符串
 * 
 * @param str 字符串
 */
void Bsp_Print_Puts(const char *str)
{
    size_t len;

    if (!str)
    {
        return;
    }

    len = strlen(str);

    HAL_UART_Transmit(&huart2, (uint8_t *)str, len, 10 * len);
}

/**
 * @brief 格式化打印
 * 
 * @param format 格式化字符串
 * @param ... 可变参数
 */
void Bsp_Print_Printf(const char *format, ...)
{
    int32_t cnt = 0;
    va_list args;

    if (!format)
    {
        return;
    }

    va_start(args, format);
    cnt = vsnprintf((char *)print_buf, PRINT_BUF_MAX, format, args);
    va_end(args);

    if (cnt > 0 && cnt < PRINT_BUF_MAX)
    {
        HAL_UART_Transmit(&huart2, print_buf, cnt, HAL_MAX_DELAY);
    }
}

/**
 * @brief 串口2中断处理服务函数
 * 
 */
void USART2_IRQHandler(void)
{
    HAL_UART_IRQHandler(&huart2);
}
